home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C++ / Snippets / Equation Evaluator / CEquation ƒ / CEquation.cp next >
Encoding:
Text File  |  1995-11-06  |  24.0 KB  |  695 lines  |  [TEXT/MPCC]

  1. /*************************************************************************
  2. **                                                                       **
  3. ** EE.C         Expression Evaluator                                     **
  4. **                                                                       **
  5. ** AUTHOR:      Mark Morley                                              **
  6. ** COPYRIGHT:   (c) 1992 by Mark Morley                                  **
  7. ** DATE:        December 1991                                            **
  8. ** HISTORY:     Jan 1992 - Made it squash all command line arguments     **
  9. **                         into one big long string.                     **
  10. **                       - It now can set/get VMS symbols as if they     **
  11. **                         were variables.                               **
  12. **                       - Changed max variable name length from 5 to 15 **
  13. **              Jun 1992 - Updated comments and docs                     **
  14. **                                                                          **
  15. **  RMD:        Apr 1995 - Ported to Macintosh for CodeWarrior           **
  16. **                         - Semicolons may now termiante expressions.     **
  17. **                           This now allows several expressions to be     **
  18. **                           given at once.                                 **
  19. **                         - Changed function pointer definition for C++     **
  20. **                         - Change int to short                             **
  21. **                         - TYPE changed from double to float             **
  22. **                         - Fixed crash with undefined functions          **
  23. **                         - Reorganized as a class                         **
  24. **                         - main() put into separate file (EEmain.cp)      **
  25. **                                                                       **
  26. ** You are free to incorporate this code into your own works, even if it **
  27. ** is a commercial application.  However, you may not charge anyone else **
  28. ** for the use of this code!  If you intend to distribute your code,     **
  29. ** I'd appreciate it if you left this message intact.  I'd like to       **
  30. ** receive credit wherever it is appropriate.  Thanks!                   **
  31. **                                                                       **
  32. ** I don't promise that this code does what you think it does...         **
  33. **                                                                       **
  34. ** Please mail any bug reports/fixes/enhancments to me at:               **
  35. **      morley@camosun.bc.ca                                             **
  36. ** or                                                                    **
  37. **      Mark Morley                                                      **
  38. **      3889 Mildred Street                                              **
  39. **      Victoria, BC  Canada                                             **
  40. **      V8Z 7G1                                                          **
  41. **      (604) 479-7861                                                   **
  42. **                                                                       **
  43. ** For comments on the Mac port                                             **
  44. **        Robert M. Douglas                                                 **
  45. **        McKellar Designs                                                 **
  46. **        rdouglas@mckellar.com    or   douglas@org.ecc.ubc.ca             **
  47. **      http://www.mckellar.com/designs/                                 **
  48. **                                                                         **
  49.  *************************************************************************/
  50.  
  51. #include <stdlib.h>
  52. #include <unix.h>
  53. #include <string.h>
  54. #include <math.h>
  55.  
  56. #include "CEquation.h"
  57.  
  58. /*************************************************************************
  59. **                                                                       **
  60. ** VARIABLE DECLARATIONS                                                 **
  61. **                                                                       **
  62.  *************************************************************************/
  63.  
  64. /*
  65.    Add any "constants" here...  These are "read-only" values that are
  66.    provided as a convienence to the user.  Their values can not be
  67.    permanently changed.  The first field is the variable name, the second
  68.    is its value.
  69. */
  70. VARIABLE CEquation::Consts[] =
  71. {
  72.    /* name, value */
  73.    { "pi",      M_PI },
  74.    { "e",       M_E },
  75.  
  76.    { 0 }
  77. };
  78.  
  79. /*
  80.    Add any math functions that you wish to recognise here...  The first
  81.    field is the name of the function as it would appear in an expression.
  82.    The second field tells how many arguments to expect.  The third is
  83.    a pointer to the actual function to use.
  84.    
  85.    FunctionPtr definition added by RMD
  86. */
  87. FUNCTION CEquation::Funcs[] =
  88. {
  89.    /* name, funtion to call */
  90.    { "sin",     1,    (FunctionPtr)sin },
  91.    { "cos",     1,    (FunctionPtr)cos },
  92.    { "tan",     1,    (FunctionPtr)tan },
  93.    { "asin",    1,    (FunctionPtr)asin },
  94.    { "acos",    1,    (FunctionPtr)acos },
  95.    { "atan",    1,    (FunctionPtr)atan },
  96.    { "sinh",    1,    (FunctionPtr)sinh },
  97.    { "cosh",    1,    (FunctionPtr)cosh },
  98.    { "tanh",    1,    (FunctionPtr)tanh },
  99.    { "exp",     1,    (FunctionPtr)exp },
  100.    { "log",     1,    (FunctionPtr)log },
  101.    { "log10",   1,    (FunctionPtr)log10 },
  102.    { "sqrt",    1,    (FunctionPtr)sqrt },
  103.    { "floor",   1,    (FunctionPtr)floor },
  104.    { "ceil",    1,    (FunctionPtr)ceil },
  105.    { "abs",     1,    (FunctionPtr)fabs },
  106. //   { "hypot",   2,    (FunctionPtr)hypot },
  107.    { "deg",     1,    (FunctionPtr)deg },
  108.    { "rad",     1,    (FunctionPtr) rad },
  109.  
  110.    { "",        0,        nil }
  111. };
  112.  
  113.  
  114.  
  115. /*************************************************************************
  116. **                                                                       **
  117. ** Some custom math functions...   Note that they must be prototyped     **
  118. ** above (if your compiler requires it)                                  **
  119. **                                                                       **
  120. ** deg( x )             Converts x radians to degrees.                   **
  121. ** rad( x )             Converts x degrees to radians.                   **
  122. **                                                                       **
  123.  *************************************************************************/
  124.  
  125. double
  126. deg( double x )
  127. {
  128.    return( x * 180.0 / M_PI );
  129. }
  130.  
  131. double
  132. rad( double x )
  133. {
  134.    return( x * M_PI / 180.0 );
  135. }
  136.  
  137.  
  138. /*************************************************************************
  139. **                                                                       **
  140. ** Equation Evaluator constructor and destructor                         **
  141. **                                                                       **
  142. **    RMD April 14, 1995                                                     **
  143. **                                                                       **
  144.  ************************************************************************/
  145. CEquation::CEquation()
  146. {
  147. }
  148.  
  149. CEquation::~CEquation()
  150. {
  151. }
  152.  
  153. /*************************************************************************
  154. **                                                                       **
  155. ** GetSymbol( char* s )                                                  **
  156. **                                                                       **
  157. ** This routine obtains a value from the program's environment.          **
  158. ** This works for DOS and VMS (and other OS's???)
  159. **                                                                       **
  160.  ************************************************************************/
  161.  
  162. short CEquation::GetSymbol( char* s, TYPE* v )
  163. {
  164.    char* e;
  165.  
  166.    e = getenv( s );
  167.    if( !e )
  168.       return( 0 );
  169.    *v = atof( e );
  170.    return( 1 );
  171. }
  172.  
  173.  
  174. /*************************************************************************
  175. **                                                                       **
  176. ** SetSymbol( char* s, char* v )                                         **
  177. **                                                                       **
  178. ** This VMS specific routine sets (or updates) a VMS symbol to a given   **
  179. ** value                                                                 **
  180. **                                                                       **
  181.  *************************************************************************/
  182.  
  183. #ifdef VAX
  184. SetSymbol( char* s, char* v )
  185. {
  186.    struct dsc$descriptor_s sym;
  187.    struct dsc$descriptor_s val;
  188.    long                    typ = 1;
  189.  
  190.    sym.dsc$w_length = strlen( s );
  191.    sym.dsc$a_pointer = s;
  192.    sym.dsc$b_class = DSC$K_CLASS_S;
  193.    sym.dsc$b_dtype = DSC$K_DTYPE_T;
  194.    val.dsc$w_length = strlen( v );
  195.    val.dsc$a_pointer = v;
  196.    val.dsc$b_class = DSC$K_CLASS_S;
  197.    val.dsc$b_dtype = DSC$K_DTYPE_T;
  198.    return( LIB$SET_SYMBOL( &sym, &val, &typ ) );
  199. }
  200. #endif
  201.  
  202.  
  203. /*************************************************************************
  204. **                                                                       **
  205. ** strlwr( char* s )   Internal use only                                 **
  206. **                                                                       **
  207. ** This routine converts a string to lowercase.  I know many compilers   **
  208. ** offer their own routine, but my VMS compiler didn't so...             **
  209. ** Again, this one is ASCII specific!                                    **
  210. **                                                                       **
  211.  *************************************************************************/
  212.  
  213. static void
  214. strlwr( char* s )
  215. {
  216.    while( *s )
  217.    {
  218.       if( *s >= 'A' && *s <= 'Z' )
  219.          *s += 32;
  220.       s++;
  221.    }
  222. }
  223.  
  224.  
  225. /*************************************************************************
  226. **                                                                       **
  227. ** ClearAllVars()                                                        **
  228. **                                                                       **
  229. ** Erases all user-defined variables from memory. Note that constants    **
  230. ** can not be erased or modified in any way by the user.                 **
  231. **                                                                       **
  232. ** Returns nothing.                                                      **
  233. **                                                                       **
  234.  *************************************************************************/
  235.  
  236. void CEquation::ClearAllVars(void)
  237. {
  238.    short i;
  239.  
  240.    for( i = 0; i < MAXVARS; i++ )
  241.    {
  242.       *mVars[i].name = 0;
  243.       mVars[i].value = 0;
  244.    }
  245. }
  246.  
  247.  
  248. /*************************************************************************
  249. **                                                                       **
  250. ** ClearVar( char* name )                                                **
  251. **                                                                       **
  252. ** Erases the user-defined variable that is called NAME from memory.     **
  253. ** Note that constants are not affected.                                 **
  254. **                                                                       **
  255. ** Returns 1 if the variable was found and erased, or 0 if it didn't     **
  256. ** exist.                                                                **
  257. **                                                                       **
  258.  *************************************************************************/
  259.  
  260. Boolean CEquation::ClearVar( char* name )
  261. {
  262.    short i;
  263.  
  264.    for( i = 0; i < MAXVARS; i++ )
  265.       if( *mVars[i].name && ! strcmp( name, mVars[i].name ) )
  266.       {
  267.          *mVars[i].name = 0;
  268.          mVars[i].value = 0;
  269.          return true;
  270.       }
  271.    return false;
  272. }
  273.  
  274.  
  275. /*************************************************************************
  276. **                                                                       **
  277. ** GetValue( char* name, TYPE* value )                                   **
  278. **                                                                       **
  279. ** Looks up the specified variable (or constant) known as NAME and       **
  280. ** returns its contents in VALUE.                                        **
  281. **                                                                       **
  282. ** First the user-defined variables are searched, then the constants are **
  283. ** searched.                                                             **
  284. **                                                                       **
  285. ** Returns 1 if the value was found, or 0 if it wasn't.                  **
  286. **                                                                       **
  287.  *************************************************************************/
  288.  
  289. Boolean CEquation::GetValue( char* name, TYPE* value )
  290. {
  291.    short i;
  292.  
  293.    /* First check for an environment variable reference... */
  294.    if( *name == '_' )
  295.       return( GetSymbol( name + 1, value ) );
  296.  
  297.    /* Now check the user-defined variables. */
  298.    for( i = 0; i < MAXVARS; i++ )
  299.       if( *mVars[i].name && ! strcmp( name, mVars[i].name ) )
  300.       {
  301.          *value = mVars[i].value;
  302.          return true;
  303.       }
  304.  
  305.    /* Now check the programmer-defined constants. */
  306.    for( i = 0; *Consts[i].name; i++ )
  307.       if( *Consts[i].name && ! strcmp( name, Consts[i].name ) )
  308.       {
  309.          *value = Consts[i].value;
  310.          return true;
  311.       }
  312.    return false;
  313. }
  314.  
  315.  
  316. /*************************************************************************
  317. **                                                                       **
  318. ** SetValue( char* name, TYPE* value )                                   **
  319. **                                                                       **
  320. ** First, it erases any user-defined variable that is called NAME.  Then **
  321. ** it creates a new variable called NAME and gives it the value VALUE.   **
  322. **                                                                       **
  323. ** Returns 1 if the value was added, or 0 if there was no more room.     **
  324. **                                                                       **
  325.  *************************************************************************/
  326.  
  327. Boolean CEquation::SetValue( char* name, TYPE* value )
  328. {
  329.    char b[30];
  330.    short  i;
  331.  
  332. #ifdef VAX
  333.    if( *name == '_' )
  334.    {
  335.       sprintf( b, "%g", *value );
  336.       if( SetSymbol( name + 1, b ) != SS$_NORMAL )
  337.          return false;
  338.       return true;
  339.    }
  340. #endif
  341.    ClearVar( name );
  342.    for( i = 0; i < MAXVARS; i++ )
  343.       if( ! *mVars[i].name )
  344.       {
  345.          strcpy( mVars[i].name, name );
  346.          mVars[i].name[VARLEN] = 0;
  347.          mVars[i].value = *value;
  348.          return true;
  349.       }
  350.    return false;
  351. }
  352.  
  353.  
  354. /*************************************************************************
  355. **                                                                       **
  356. ** Parse()   Internal use only                                           **
  357. **                                                                       **
  358. ** This function is used to grab the next token from the expression that **
  359. ** is being evaluated.                                                   **
  360. **                                                                       **
  361.  *************************************************************************/
  362.  
  363. void CEquation::Parse(void)
  364. {
  365.    char* t;
  366.  
  367.    mTokenType = 0;
  368.    t = mToken;
  369.    if (*mSource == ';') return;
  370.    while( iswhite( *mSource ) )
  371.       mSource++;
  372.    if( isdelim( *mSource ) )
  373.    {
  374.       mTokenType = DEL;
  375.       *t++ = *mSource++;
  376.    }
  377.    else if( isnumer( *mSource ) )
  378.    {
  379.       mTokenType = NUM;
  380.       while( isnumer( *mSource ) )
  381.          *t++ = *mSource++;
  382.    }
  383.    else if( isalpha( *mSource ) )
  384.    {
  385.       mTokenType = VAR;
  386.       while( isalpha( *mSource ) )
  387.         *t++ = *mSource++;
  388.       mToken[VARLEN] = 0;
  389.    }
  390.    else if( *mSource )
  391.    {
  392.       *t++ = *mSource++;
  393.       *t = 0;
  394.       Throw( E_SYNTAX );
  395.    }
  396.    *t = 0;
  397.    while( iswhite( *mSource ) )
  398.       mSource++;
  399. }
  400.  
  401.  
  402. /*************************************************************************
  403. **                                                                       **
  404. ** Level1( TYPE* r )   Internal use only                                 **
  405. **                                                                       **
  406. ** This function handles any variable assignment operations.             **
  407. ** It returns a value of 1 if it is a top-level assignment operation,    **
  408. ** otherwise it returns 0                                                **
  409. **                                                                       **
  410.  *************************************************************************/
  411.  
  412. Boolean    CEquation::Level1( TYPE* r )
  413. {
  414.    char t[VARLEN + 1];
  415.  
  416.    if( mTokenType == VAR )
  417.       if( *mSource == '=' )
  418.       {
  419.          strcpy( t, mToken );
  420.          Parse();
  421.          Parse();
  422.          if( !*mToken )
  423.          {
  424.             ClearVar( t );
  425.             return(1);
  426.          }
  427.          Level2( r );
  428.          if( ! SetValue( t, r ) )
  429.             Throw( E_MAXVARS );
  430.          return( 1 );
  431.       }
  432.    Level2( r );
  433.    return( 0 );
  434. }
  435.  
  436.  
  437. /*************************************************************************
  438. **                                                                       **
  439. ** Level2( TYPE* r )   Internal use only                                 **
  440. **                                                                       **
  441. ** This function handles any addition and subtraction operations.        **
  442. **                                                                       **
  443.  *************************************************************************/
  444.  
  445. void    CEquation::Level2( TYPE* r )
  446. {
  447.    TYPE t = 0;
  448.    char o;
  449.  
  450.    Level3( r );
  451.    while( (o = *mToken) == '+' || o == '-' )
  452.    {
  453.       Parse();
  454.       Level3( &t );
  455.       if( o == '+' )
  456.          *r = *r + t;
  457.       else if( o == '-' )
  458.          *r = *r - t;
  459.    }
  460. }
  461.  
  462.  
  463. /*************************************************************************
  464. **                                                                       **
  465. ** Level3( TYPE* r )   Internal use only                                 **
  466. **                                                                       **
  467. ** This function handles any multiplication, division, or modulo.        **
  468. **                                                                       **
  469.  *************************************************************************/
  470.  
  471. void    CEquation::Level3( TYPE* r )
  472. {
  473.    TYPE t;
  474.    char o;
  475.  
  476.    Level4( r );
  477.    while( (o = *mToken) == '*' || o == '/' || o == '%' )
  478.    {
  479.       Parse();
  480.       Level4( &t );
  481.       if( o == '*' )
  482.          *r = *r * t;
  483.       else if( o == '/' )
  484.       {
  485.          if( t == 0 )
  486.             Throw( E_DIVZERO );
  487.          *r = *r / t;
  488.       }
  489.       else if( o == '%' )
  490.       {
  491.          if( t == 0 )
  492.             Throw( E_DIVZERO );
  493.          *r = fmod( *r, t );
  494.       }
  495.    }
  496. }
  497.  
  498.  
  499. /*************************************************************************
  500. **                                                                       **
  501. ** Level4( TYPE* r )   Internal use only                                 **
  502. **                                                                       **
  503. ** This function handles any "to the power of" operations.               **
  504. **                                                                       **
  505.  *************************************************************************/
  506.  
  507. void    CEquation::Level4( TYPE* r )
  508. {
  509.    TYPE t;
  510.  
  511.    Level5( r );
  512.    if( *mToken == '^' )
  513.    {
  514.       Parse();
  515.       Level5( &t );
  516.       *r = pow( *r, t );
  517.    }
  518. }
  519.  
  520.  
  521. /*************************************************************************
  522. **                                                                       **
  523. ** Level5( TYPE* r )   Internal use only                                 **
  524. **                                                                       **
  525. ** This function handles any unary + or - signs.                         **
  526. **                                                                       **
  527.  *************************************************************************/
  528.  
  529. void    CEquation::Level5( TYPE* r )
  530. {
  531.    char o = 0;
  532.  
  533.    if( *mToken == '+' || *mToken == '-' )
  534.    {
  535.       o = *mToken;
  536.       Parse();
  537.    }
  538.    Level6( r );
  539.    if( o == '-' )
  540.       *r = -*r;
  541. }
  542.  
  543.  
  544. /*************************************************************************
  545. **                                                                       **
  546. ** Level6( TYPE* r )   Internal use only                                 **
  547. **                                                                       **
  548. ** This function handles any literal numbers, variables, or functions.   **
  549. **                                                                       **
  550.  *************************************************************************/
  551.  
  552. void    CEquation::Level6( TYPE* r )
  553. {
  554.    short  i;
  555.    short  n;
  556.    TYPE a[3];
  557.  
  558.    if( *mToken == '(' )
  559.    {
  560.       Parse();
  561.       if( *mToken == ')' )
  562.          Throw( E_NOARG );
  563.       Level1( r );
  564.       if( *mToken != ')' )
  565.          Throw( E_UNBALAN );
  566.       Parse();
  567.    }
  568.    else
  569.    {
  570.       if( mTokenType == NUM )
  571.       {
  572.          *r = (TYPE) atof( mToken );
  573.          Parse();
  574.       }
  575.       else if( mTokenType == VAR )
  576.       {
  577.          if( *mSource == '(' )
  578.          {
  579.             for( i = 0; Funcs[i].args; i++ )
  580.                if( ! strcmp( mToken, Funcs[i].name ) )
  581.                {
  582.                   Parse();
  583.                   n = 0;
  584.                   do
  585.                   {
  586.                      Parse();
  587.                      if( *mToken == ')' || *mToken == ',' )
  588.                         Throw( E_NOARG );
  589.                      a[n] = 0;
  590.                      Level1( &a[n] );
  591.                      n++;
  592.                   } while( n < 4 && *mToken == ',' );
  593.                   Parse();
  594.                   if( n != Funcs[i].args )
  595.                   {
  596.                      strcpy( mToken, Funcs[i].name );
  597.                      Throw( E_NUMARGS );
  598.                   }
  599.                   *r = Funcs[i].func( a[0], a[1], a[2] );
  600.                   return;
  601.                }
  602.                if( ! *Funcs[i].name )
  603.                   Throw( E_BADFUNC );
  604.             }
  605.             else if( ! GetValue( mToken, r ) )
  606.                Throw( E_UNKNOWN );
  607.          Parse();
  608.       }
  609.       else
  610.          Throw( E_SYNTAX );
  611.    }
  612. }
  613.  
  614.  
  615. /*************************************************************************
  616. **                                                                       **
  617. ** Evaluate( char* e, TYPE* result, short* a )                             **
  618. **                                                                       **
  619. ** This function is called to evaluate the expression E and return the   **
  620. ** answer in RESULT.  If the expression was a top-level assignment, a    **
  621. ** value of 1 will be returned in A, otherwise it will contain 0.        **
  622. **                                                                       **
  623. ** Returns E_OK if the expression is valid, or an error code.            **
  624. **                                                                       **
  625.  *************************************************************************/
  626.  
  627.  
  628.  
  629. Boolean    CEquation::Evaluate( char* e, TYPE* result, short* a )
  630. {
  631.     Try_ {
  632.        mSource = e;
  633.        mErrorFrom = mSource;
  634.        while (*mSource != 0) {
  635.            strlwr( mSource );
  636.            *result = 0;
  637.            Parse();
  638.            if( ! *mToken ) Throw(E_EMPTY);
  639.            *a = Level1( result );
  640.            while ((*mSource == ';') || iswhite( *mSource ))
  641.                  mSource++;    
  642.         }
  643.     }
  644.     Catch_(err) {
  645.           mErrorCode = err;
  646.           return( err );
  647.     } EndCatch_
  648.    return  E_OK;
  649. }
  650.  
  651.  
  652. /*************************************************************************
  653. **                                                                       **
  654. ** What follows is a main() routine that evaluates the command line      **
  655. ** arguments one at a time, and displays the results of each expression. **
  656. ** Without arguments, it becomes an interactive calculator.              **
  657. **                                                                       **
  658.  *************************************************************************/
  659.  
  660. #include <stdio.h>
  661.  
  662. char* CEquation::ErrMsgs[] =
  663. {
  664.    "Syntax error",
  665.    "Unbalanced parenthesis",
  666.    "Division by zero",
  667.    "Unknown variable",
  668.    "Maximum variables exceeded",
  669.    "Unrecognised funtion",
  670.    "Wrong number of arguments to funtion",
  671.    "Missing an argument",
  672.    "Empty expression"
  673. };
  674.  
  675. void CEquation::ReportError(void)
  676. {
  677.     printf( "ERROR: %s - %s", ErrMsgs[mErrorCode - 1], mToken );
  678.     printf( "\n%s", mErrorFrom );
  679.     printf( "\n%*s^\n", mSource - mErrorFrom - 1, "" );
  680. }
  681.  
  682.  
  683. void CEquation::ListVariables(void)
  684. {
  685.      for(short i = 0; i < MAXVARS; i++ )
  686.         if( *mVars[i].name )
  687.            printf( "%s = %g\n", mVars[i].name, mVars[i].value );
  688. }
  689.  
  690. void CEquation::ListConstants(void)
  691. {
  692.       for(short i = 0; *Consts[i].name; i++ )
  693.         printf( "%s = %g\n", Consts[i].name, Consts[i].value );
  694. }
  695.